#!/usr/bin/python
#-*- coding: latin-1 -*-

from sqlite3 import *
#from lib.database import database

class Markov():
	def __init__(self, wordSize=5, dataBase=":memory:"):
		self.wordSize = wordSize
		self.dataBase = connect(dataBase)
		self.dataBase.isolation_level = None
		self.dataBase.text_factory = str
		self.db = self.dataBase.cursor()
		self.tableName = "word%d" % self.wordSize
		try:
			SQL = "SELECT * FROM %d" % self.tableName
			self.db.execute(SQL)
		except:
			print "Initializing Database: ",
			self.__createDataStructure()
			print "done."

	def __createDataStructure(self):
		SQL = "CREATE TABLE IF NOT EXISTS %s ( id INTEGER PRIMARY KEY" % self.tableName
		for i in xrange(self.wordSize):
			SQL += ", char%d " % i
		SQL += ", num INTEGER DEFAULT 1, miss INTEGER DEFAULT 0 )"
		try:
			self.db.execute( SQL )
		except OperationalError, msg:
			print "Error creating table: ", self.tableName, msg
	
	def createIndex(self):
		SQL = "CREATE INDEX IF NOT EXISTS %s_index on %s ( " % (self.tableName, self.tableName)
		for i in xrange(self.wordSize):
			SQL += "char%d, " % i
		SQL += " num )"
		print "Creating index: ",
		try:
			self.db.execute( SQL )
			print "done."
		except OperationalError, msg:
			print "Error.\nError creating index: index_", self.tableName
			print msg, SQL
	
	def __addRow(self, values, n = 1, m = 0):
		tmp = values[:]
		SQL = "INSERT INTO %s ( " % self.tableName
		for i in xrange(self.wordSize):
			SQL += "char%d, " % i
		SQL += " num, miss) VALUES ( "
		for c in values:
			SQL += "?,"
		SQL += " ?, ? )"
		tmp.append(n)
		tmp.append(m)
		self.db.execute( SQL, tmp )
	
	def __updateRow(self, values, n=1):
		tmp = values[:]
		SQL = "UPDATE %s SET num = ? WHERE " % self.tableName
		for i in xrange(len(values)):
			SQL += """char%d = ? AND """ % i
			i += 1 
		SQL = SQL[:-4]
		tmp.insert(0,n)
		self.db.execute( SQL, tmp )
	
	def __rowNum(self, values):
		SQL = "SELECT num FROM %s WHERE " % self.tableName
		for i in xrange(len(values)):
			SQL += """char%d = ? AND """ % i
		SQL = SQL[:-4]
		self.db.execute( SQL, values )
		result = self.db.fetchone()
		if result is None: return 0
		return result[0]
	
	def __sumRows(self, values):
		SQL = "SELECT sum(num) FROM %s WHERE " % self.tableName
		for i in xrange(len(values)):
			SQL += """char%d = ? AND """ % i
		SQL = SQL[:-4]
		self.db.execute( SQL, values )
		result = self.db.fetchone()
		if result[0] is None: return 1
		return result[0]
	
	def __genCharSequence(self, toLearn):
		temp = list()
		toLearn.insert(0,'!^ ')
		toLearn.append(' $!')
		while ( len(toLearn) > 0 ):
			temp.append(toLearn.pop(0))
			if ( len(temp) >= self.wordSize):
				yield temp
				temp.pop(0)
	
	def __genWordSequence(self, toLearn):
		temp = list()
		toLearn=(''.join(toLearn)).split()
		toLearn.insert(0,'!^ ')
		toLearn.append(' $!')
		while ( len(toLearn) > 0 ):
			temp.append(toLearn.pop(0))
			if ( len(temp) >= self.wordSize):
				yield temp
				temp.pop(0)
	
	def __addSpaces(self, seq, ret, deep=1, c=1):
		if deep < c: return
		for i in xrange(len(seq)):
			tmp = seq[:]
			if tmp[i] == '' or i==0 and tmp[0]=='!^ ' : break
			tmp[i] = ''
			ret.append([tmp,c])
			self.__addSpaces(tmp,ret,deep,c+1)
	
	def __train(self,toLearn, deep=0):
		#for seq in self.__genCharSequence(toLearn):
		for seq in self.__genWordSequence(toLearn):
			self.__addRow(seq)
			#tmp = []
			#tmp.append([seq[:-1],0])
			#self.__addSpaces(seq[:-1],tmp,deep)
			#for comb,miss in tmp:
				#if len(comb) == miss: break
				#comb.extend(seq[-1:])
				#num = self.__rowNum(comb)
				#if num > 0: self.__updateRow(comb,num + 1)
				#else: self.__addRow(comb,num + 1, miss)
				#self.__addRow(comb, 1, miss)
	
	def trainText(self, text, deep=0):
		toLearn = list(text)
		self.__train(toLearn, deep)
		self.dataBase.commit()
		self.db.execute("VACUUM")
	
	def trainFile(self, filePath, deep=0):
		from sys import stdout
		try:
			f = open(filePath, 'r')
		except IOError:
			print "File not found."
			return
		lines = 0.0
		for line in f:
			lines += 1
		f.seek(0)
		stat = 0.0
		print "\rLearning status: %6.2f%% completed." % (100 * stat/lines),
		for line in f:
			if len(line) < 2: break
			self.__train(list(line), deep)
			stat += 1
			print "\rLeaning status: %6.2f%% completed." % (100 * stat/lines),
			stdout.flush()
		print
		self.dataBase.commit()
		self.db.execute("VACUUM")
		f.close
	
	def guessChar(self, prev=['!^ ']):
		from random import randint
		SQL = "SELECT char%d FROM %s WHERE " % (len(prev), self.tableName)
		for i in xrange(len(prev)):
			SQL += """char%d = ? AND """ % i
		SQL = SQL[:-4]
		SQL += "ORDER BY id DESC LIMIT 1 OFFSET %d" % randint(0, self.__sumRows(prev) -1)
		self.db.execute( SQL, prev )
		result = self.db.fetchone()
		if result is None:
			return ' $!'
		return result[0]

	def guessSentence(self, prev=['!^ ']):
		sentence = []
		while sentence[-1:] != [' $!']:
			sentence.append(self.guessChar(prev))
			n = -1 * self.wordSize +1 
			prev = sentence[n:]
		return sentence[:-1]
